package uva;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Random;

import rice.p2p.commonapi.Id;
import rice.pastry.NodeHandle;
import rice.pastry.routing.RoutingTable;

public class RoutingTableManager {
	HashMap<rice.p2p.commonapi.Id, Integer> mappings;
	private RoutingTable tables[];
	private RoutingTable reconciledTable;
	private rice.p2p.commonapi.Id myId;

	public RoutingTableManager(RoutingTable table) {

		reconciledTable = new RoutingTable(table.myNodeHandle, table.getMaxEntries(), table.baseBitLength(), table.pn, false);
		mappings = new HashMap<Id, Integer>();
		setTables(new RoutingTable[3]);

		for (int i = 0; i < getTables().length; i++) {
			getTables()[i] = new RoutingTable(table.myNodeHandle, table.getMaxEntries(), table.baseBitLength(), table.pn, false);
		}
	}

	public RoutingTable getMainTable() {
		return reconciledTable;
	}

	public void Put(NodeHandle nh) {
		Random random = new Random(new Date().getTime());
		int tableIndex = random.nextInt(tables.length);

		if (nh.isAlive()) {
			RoutingTable tables[] = getTables();
			tables[tableIndex].put(nh, null);
			// tables[tableIndex].put(nh);
		}
	}

	public boolean Put(int tableIndex, NodeHandle nh, rice.p2p.commonapi.Id senderId) {

		if (senderId != null && reconciledTable.myNodeHandle.getId().toStringFull().equals(senderId.toStringFull())) {
			// System.out.println("we are still doing the join protocol for " + senderId.toStringFull());
			return reconciledTable.put(nh, null);

		}



		if (nh.isAlive() && !reconciledTable.printSelfFull().contains(nh.getId().toStringFull())) {
			if (MaliciousNodes.getInstance().isPresent(nh.getId())) {
				System.out.print("Adding >!" + nh.getId().toStringFull() + " to ");

				if (MaliciousNodes.getInstance().isPresent(reconciledTable.myNodeHandle.getId())) {
					System.out.print(">!" + reconciledTable.myNodeHandle.getId().toStringFull() + "!< from ");
				}
				else {
					System.out.print(reconciledTable.myNodeHandle.getId().toStringFull() + " from ");
				}

				if (MaliciousNodes.getInstance().isPresent(senderId)) {
					System.out.println(">!" + senderId.toStringFull() + "!<");
				}
				else {
					System.out.println(senderId.toStringFull());
				}
			}
			
			RoutingTable tables[] = getTables();
			tables[tableIndex].put(nh, null);
			// tables[tableIndex].put(nh);
		}

		int count = 0;
		for (RoutingTable table : tables) {
			if (table.printSelfFull().contains(nh.getId().toStringFull())) {
				count++;
			}
		}

		if (count > tables.length / 2) {
			for (RoutingTable table : getTables()) {
				table.printSelfFull();
				table.remove(nh);
				table.printSelfFull();
			}
			
			return reconciledTable.put(nh, null);
		}

		return true;
	}

	public int GetTableIndex() {
		Random random = new Random(new Date().getTime());
		int tableIndex = random.nextInt(tables.length);
		return tableIndex;
	}

	public int GetTableIndexBasedOnSource(rice.p2p.commonapi.Id sourceId) {
		for (int i = 0; i < getTables().length; i++) {
			RoutingTable table = getTables()[i];

			if (table.printSelfFull().contains(sourceId.toStringFull())) {
				// System.out.println(table.printSelf());
				// System.out.println("sourceId: " + sourceId + "index: " + i);
				return i;
			}
		}

		int currentSmallest = 0;

		for (int i = 0; i < getTables().length; i++) {
			RoutingTable table = getTables()[i];
			if (table.numEntries() < getTables()[currentSmallest].numEntries()) {
				currentSmallest = i;
			}
		}
		return currentSmallest;
	}

	public int GetTableIndex(rice.p2p.commonapi.Id id) {
		if (mappings.containsKey(id)) {
			return mappings.get(id);
		}

		int tableIndex = determineTableIndexForNewKey();

		if (tableIndex == -1) {
			tableIndex = 0;
		}
		mappings.put(id, tableIndex);

		return tableIndex;
	}

	private int determineTableIndexForNewKey() {

		Collection<Integer> values = mappings.values();
		ArrayList<Integer> list = new ArrayList<Integer>(values);

		for (int i = 0; i < getTables().length; i++) {
			if (!list.contains(i)) {
				return i;
			}
		}

		Collections.sort(list);

		int currentlyChecking = list.get(0);
		int currentCount = 1;
		int currentSmallestCount = Integer.MAX_VALUE;
		int currentSmallestIndex = 0;

		for (int i = 1; i < list.size(); i++) {
			if (list.get(i) == currentlyChecking) {
				currentCount++;
			}
			else if (currentCount < currentSmallestCount) {
				currentSmallestCount = currentCount;
				currentSmallestIndex = currentlyChecking;

				currentlyChecking = list.get(i);
				currentCount = 1;
			}
			else {// neither the same and smallest, we just moved to the next without finding a smaller index
				currentlyChecking = list.get(i);
				currentCount = 1;

			}

		}

		if (currentCount < currentSmallestCount) {
			return currentlyChecking;
		}

		return currentSmallestIndex;
	}

	public RoutingTable getTable(int index) {
		if (index == -1) {
			return reconciledTable;
		}
		else {
			return tables[index];
		}
	}

	public RoutingTable[] getTables() {
		return tables;
	}

	public void setTables(RoutingTable tables[]) {
		this.tables = tables;
	}

}
